#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/uaccess.h>

static dev_t demo_drv_devno;
static struct class* demo_drv_cls;
static const char* demo_drv_mod_name = "demo_drv_mod";
static const char* demo_drv_cls_name = "demo_drv_cls";
static const char* demo_drv_dev_name = "demo_drv_dev";
static struct cdev demo_drv_cdev;

static char name_buf[1024] = {0};

//如果执行cat /dev/demo_drv_dev命令，只要返回值不为0，就会一直cat，所以必须要有一个返回0的分支
static ssize_t demo_drv_read(struct file* filp, char __user* buf, size_t size, loff_t* ppos)
{
    int ret = 0;
    size_t len = strlen(name_buf);
    unsigned long p = *ppos;
    unsigned long count = 0;

    printk("p = %ld, len = %ld\n", p, len);

    if (p >= len)
    {
        return 0;
    }

    if (size >= len - p)
    {
        count = len - p;
    }
    else
    {
        count = size;
    }

    if (copy_to_user(buf, name_buf + p, count))
    {
        ret = -EFAULT;
    }
    else
    {
        *ppos += count;
        ret = count;
    }

    return ret;
}

static ssize_t demo_drv_write(struct file* filp, const char __user* buf, size_t size, loff_t* ppos)
{
    int ret = 0;

    if (copy_from_user(name_buf, buf, size))
    {
        ret = -EFAULT;
    }
    else
    {
        name_buf[size] = '\0';
        ret = size;
    }

    {
        char* p = name_buf;
        for (; *p != '\0'; p++)
        {
            if (*p >= 'a' && *p <= 'z')
            {
                *p = *p - 'a' + 'A';
            }
        }
    }
    

    return ret;
}

static struct file_operations demo_drv_ops=
{
    .owner = THIS_MODULE,
	.read = demo_drv_read,
    .write = demo_drv_write
};

static int demo_drv_probe(struct platform_device *pdev)
{
    int result;
    struct device* demo_drv_dev;

	printk("match ok!\n");

    result = alloc_chrdev_region(&demo_drv_devno, 0, 1, demo_drv_mod_name);
    if (result < 0)
	{
		printk("unable to get major for demo devs\n");
		return result;
	}

    cdev_init(&demo_drv_cdev, &demo_drv_ops);
    result = cdev_add(&demo_drv_cdev, demo_drv_devno, 1);
	if (result < 0)
	{
		printk("cdev_add error.\n");
        unregister_chrdev_region(demo_drv_devno, 1);
		return result;
	}

	demo_drv_cls = class_create(THIS_MODULE, demo_drv_cls_name);
	if (IS_ERR(demo_drv_cls))
	{
		printk("class_create error!\n");
        cdev_del(&demo_drv_cdev);
        unregister_chrdev_region(demo_drv_devno, 1);
		return PTR_ERR(demo_drv_cls);
	}

    demo_drv_dev = device_create(demo_drv_cls, NULL, demo_drv_devno, NULL, demo_drv_dev_name);
    if (IS_ERR(demo_drv_dev))
    {
        printk("device_create error!\n");
        class_destroy(demo_drv_cls);
        cdev_del(&demo_drv_cdev);
        unregister_chrdev_region(demo_drv_devno, 1);
        return PTR_ERR(demo_drv_dev);
    }

    return 0;
}

static int demo_drv_remove(struct platform_device *pdev)
{
    device_destroy(demo_drv_cls, demo_drv_devno);
    class_destroy(demo_drv_cls);
    cdev_del(&demo_drv_cdev);
    unregister_chrdev_region(demo_drv_devno, 1);

    return 0;
}

static struct platform_driver demo_drv_driver =
{
    .driver.name = "bigbang",
    .probe = demo_drv_probe,
    .remove = demo_drv_remove,
};

static int __init demo_drv_init(void)
{
	printk("demo_drv_init\n");
	
	return platform_driver_register(&demo_drv_driver);
}
 
static void __exit demo_drv_exit(void)
{
	printk("demo_drv_exit\n");
	platform_driver_unregister(&demo_drv_driver);
	
	return;
}
 
 
MODULE_LICENSE("GPL");
module_init(demo_drv_init);
module_exit(demo_drv_exit);

